﻿using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;

namespace Nirvana
{
    public abstract class SceneGridEditor : Editor
    {
        protected abstract ISceneGridMode EditMode { get; }
        protected abstract bool DrawEditMode();

        public static bool IsPainting = false;


        private SerializedProperty baseHeight;
        private SerializedProperty groundMask;
        private SerializedProperty editing;
        private SerializedProperty hide;

        private static bool isOutputFocusPos;
        private static Vector2 outputFocusPos = Vector2.zero;
        private static bool isRecordFocusPos;
        private static List<Vector2> recordPosList = new List<Vector2>();
        private static bool isEditor;
        private int gridrow, gridcolumn;
        private Vector2 cellsize;
        private bool showFog;

        //对row,col进行修改
        private int resizeX0,resizeY0,resizeX1,resizeY1;
        private void OnEnable()
        {
            this.baseHeight = serializedObject.FindProperty("baseHeight");
            this.groundMask = serializedObject.FindProperty("groundMask");
            this.editing = serializedObject.FindProperty("editing");
            this.hide = serializedObject.FindProperty("isHide");

            if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) return;
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            abstractSceneGrid.Initialize();
            abstractSceneGrid.SetGridVisible(this.editing.boolValue);
            if(this.EditMode!= null) abstractSceneGrid.FillGridColor(p => this.EditMode.GetCellColor(p));
            this.gridrow = abstractSceneGrid.Row;
            this.gridcolumn = abstractSceneGrid.Column;
            this.cellsize = abstractSceneGrid.CellSize;
            SceneView.SceneViewState viewState = SceneView.lastActiveSceneView.GetSceneViewState();
            showFog = viewState.showFog;
            viewState.showFog = false;
            if (Camera.main != null) Camera.main.depthTextureMode = DepthTextureMode.Depth;

        }

        private void OnDisable()
        {
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            abstractSceneGrid.SetGridVisible(false);
            SceneView.SceneViewState viewState = SceneView.lastActiveSceneView.sceneViewState;
            viewState.showFog = showFog;
            if (Camera.main != null) Camera.main.depthTextureMode = DepthTextureMode.Depth;
        }

        public override void OnInspectorGUI()
        {
            if (IsPainting) return;
            if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) return;
            if (Application.isPlaying) return;
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            //快捷按键时，返回
            if (Event.current.type == EventType.ValidateCommand)
            {
                abstractSceneGrid.UpdateMesh();
                if(this.EditMode != null) abstractSceneGrid.FillGridColor(p => this.EditMode.GetCellColor(p));
                return;
            }

            base.serializedObject.Update();
            EditorGUILayout.BeginHorizontal();
            var flag = EditorGUILayout.ToggleLeft("Editing", this.editing.boolValue);
     
            if (this.editing.boolValue != flag)
            {
                abstractSceneGrid.SetGridVisible(flag);
                this.editing.boolValue = flag;
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            var flag1 = EditorGUILayout.ToggleLeft("Hide", this.hide.boolValue);

            if (this.hide.boolValue != flag1)
            {
                abstractSceneGrid.SetGridHide(flag1);
                this.hide.boolValue = flag1;
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            isOutputFocusPos = EditorGUILayout.ToggleLeft("OutputFocusPos", isOutputFocusPos);
            if (isOutputFocusPos)
            {
                GUILayout.Label(string.Format("Focus Pos: {0}, {1}", outputFocusPos.x, outputFocusPos.y));
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            isRecordFocusPos = EditorGUILayout.ToggleLeft("RecordFocusPos", isRecordFocusPos);
            if (isRecordFocusPos)
            {
                if (GUILayout.Button("Clear"))
                {
                    recordPosList.Clear();
                    SaveRecordPos();
                }
                if (GUILayout.Button("Open"))
                {
                    OpenRecordPosFile();
                }
            }
            EditorGUILayout.EndHorizontal();
            GUILayout.Label("Grid Basic: ");
            GUILayoutEx.BeginContents();
            DrawGridBasic();
            GUILayoutEx.EndContents();
            EditorGUILayout.Space();
            GUILayout.Label("Grid Mode: ");
            GUILayoutEx.BeginContents();
            DrawGridMode();
            GUILayoutEx.EndContents();

            base.serializedObject.ApplyModifiedProperties();

        }

        protected void OnSceneGUI()
        {
            if (!this.editing.boolValue) return;
            if (this.EditMode == null) return;
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            if(this.resizeX0 != 0 || this.resizeX1 != 0 || this.resizeY0 != 0 || this.resizeY1 != 0)
            {
                var pos = abstractSceneGrid.transform.position;
                int x0 = -this.resizeX0;
                int y0 = -this.resizeY0;
                int x1 = abstractSceneGrid.Row + this.resizeX1 - 1;
                int y1 = abstractSceneGrid.Column + this.resizeY1 - 1;

                Vector3[] vertexs = new Vector3[5];
                vertexs[0] = pos + abstractSceneGrid.GetCellPositionLT(x0, y0);
                vertexs[1] = pos + abstractSceneGrid.GetCellPositionRT(x1, y0);
                vertexs[2] = pos + abstractSceneGrid.GetCellPositionRB(x1, y1);
                vertexs[3] = pos + abstractSceneGrid.GetCellPositionLB(x0, y1);
                vertexs[4] = vertexs[0];
                //绘制指定的抗锯齿多段线
                Color color = Handles.color;
                Handles.color = Color.magenta;
                Handles.DrawAAPolyLine(5f, vertexs);
                Handles.color = color;
            }
            //键盘操纵
            if(Event.current.type == EventType.KeyDown)
            {
                if (Event.current.keyCode == KeyCode.Minus) EditMode.IncBrushSize(-1);
                if (Event.current.keyCode == KeyCode.Equals) EditMode.IncBrushSize(1);
                if (Event.current.keyCode == KeyCode.LeftAlt) EditMode.DrawBrush(abstractSceneGrid);
            }
            //鼠标操纵
            if (Event.current.isMouse)
            {
                SceneView lastActiveSceneView = SceneView.lastActiveSceneView;
                Camera camera = lastActiveSceneView.camera;
                Vector3 pos = camera.transform.position;
                //当前鼠标位置投射射线
                Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                RaycastHit minHit = default(RaycastHit);
                var colliders =  abstractSceneGrid.GetComponentsInChildren<MeshCollider>();
                float minDist = float.PositiveInfinity;
                bool isCollider = false;
                for(int i = 0; i < colliders.Length; i++)
                {
                    RaycastHit raycastHit;
                    //检测当前collider是否和ray发生碰撞
                    if(colliders[i].Raycast(ray,out raycastHit, 100000f))
                    {
                        float dist = Vector3.Distance(pos, raycastHit.point);
                        if (dist < minDist)
                        {
                            minDist = dist;
                            minHit = raycastHit;
                            isCollider = true;
                        }
                    }
                }
                if (isCollider)
                {
                    Vector3 rootpos = abstractSceneGrid.transform.position;
                    Vector3 dir = minHit.point - rootpos;
                    EditMode.FocusPosition(abstractSceneGrid, dir);
                    int x = (int)(dir.x / abstractSceneGrid.CellSize.x);
                    int y = (int)(dir.z / abstractSceneGrid.CellSize.y);
                    if(outputFocusPos.x != x || outputFocusPos.y != y)
                    {
                        outputFocusPos.x = x;
                        outputFocusPos.y = y;
                        if (isOutputFocusPos) this.Repaint();
                    }

                }

            }
            if (isRecordFocusPos && Event.current.type == EventType.MouseUp && (outputFocusPos.x != 0 || outputFocusPos.y != 0) )
            {
                bool flag = false;
                if(recordPosList.Count > 0)
                {
                    var pos = recordPosList[recordPosList.Count - 1];
                    if (pos.x == outputFocusPos.x && pos.y == outputFocusPos.y) flag = true;
                }
                if (!flag)
                {
                    recordPosList.Add(new Vector2(outputFocusPos.x, outputFocusPos.y));
                    this.SaveRecordPos();
                }
            }

            Event current = Event.current;
            if((current.type == EventType.MouseDown || current.type == EventType.MouseDrag)
                && current.button == 0)
            {
                GUIUtility.hotControl = GUIUtility.GetControlID(FocusType.Passive);
                current.Use();
                EditMode.PaintCell(abstractSceneGrid);
                IsPainting = true;
            }
            else
            {
                EditMode.DrawBrush(abstractSceneGrid);
            }

            if(current.type == EventType.MouseUp && current.button == 0)
            {
                GUIUtility.hotControl = 0;//去焦
                IsPainting = false;
                var root = abstractSceneGrid.GetGridRoot();
                if (root != null) EditorSceneManager.MarkSceneDirty(root.scene);
            }

            EditMode.OnSceneGUI();
        }

        private void DrawGridBasic()
        {
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            this.gridrow = EditorGUILayout.IntSlider("Grid Row", this.gridrow, 1,abstractSceneGrid.MaxRow);
            this.gridcolumn = EditorGUILayout.IntSlider("Grid Column", this.gridcolumn, 1, abstractSceneGrid.MaxColumn);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Resize:");
            this.resizeX0 = EditorGUILayout.IntField(this.resizeX0);
            this.resizeX0 = Mathf.Max(this.resizeX0, -abstractSceneGrid.Row+1);
            this.resizeY0 = EditorGUILayout.IntField(this.resizeY0);
            this.resizeY0 = Mathf.Max(this.resizeY0, -abstractSceneGrid.Column + 1);
            this.resizeX1 = EditorGUILayout.IntField(this.resizeX1);
            this.resizeX1 = Mathf.Max(this.resizeX1, -abstractSceneGrid.Row + 1);
            this.resizeY1 = EditorGUILayout.IntField(this.resizeY1);
            this.resizeY1 = Mathf.Max(this.resizeY1, -abstractSceneGrid.Column + 1);
            if (GUILayout.Button("Resize"))
            {
                abstractSceneGrid.Resize(resizeX0, resizeY0, resizeX1, resizeY1);
                this.resizeX0 = 0;
                this.resizeY0 = 0;
                this.resizeX1 = 0;
                this.resizeY1 = 0;
                this.gridrow = abstractSceneGrid.Row;
                this.gridcolumn = abstractSceneGrid.Column;
                if(this.EditMode != null)
                {
                    abstractSceneGrid.FillGridColor(this.EditMode.GetCellColor);
                }

                SceneView.RepaintAll();
                EditorUtility.SetDirty(abstractSceneGrid);
                EditorSceneManager.MarkSceneDirty(abstractSceneGrid.gameObject.scene);

            }
            EditorGUILayout.EndHorizontal();

            this.cellsize = EditorGUILayout.Vector2Field("Cell Size", this.cellsize);
            EditorGUILayout.PropertyField(this.groundMask);
            EditorGUILayout.PropertyField(this.baseHeight);

            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("Rebuild") 
                && this.gridrow != abstractSceneGrid.Row 
                || this.gridcolumn != abstractSceneGrid.Column
                || this.cellsize != abstractSceneGrid.CellSize)
            {
                abstractSceneGrid.CellSize = this.cellsize;
                abstractSceneGrid.Resize(this.gridrow, this.gridcolumn);
                if(this.EditMode!= null) abstractSceneGrid.FillGridColor(p => this.EditMode.GetCellColor(p));
                SceneView.RepaintAll();
                EditorUtility.SetDirty(abstractSceneGrid);
                EditorSceneManager.MarkSceneDirty(abstractSceneGrid.gameObject.scene);
            }
            if(GUILayout.Button("Fit to Ground"))
            {
                abstractSceneGrid.FitToGround();
                if (this.EditMode != null) abstractSceneGrid.FillGridColor(p => this.EditMode.GetCellColor(p));
                SceneView.RepaintAll();
                EditorUtility.SetDirty(abstractSceneGrid);
                EditorSceneManager.MarkSceneDirty(abstractSceneGrid.gameObject.scene);
            }
            EditorGUILayout.EndHorizontal();

        }

        private void DrawGridMode()
        {
            AbstractSceneGrid abstractSceneGrid = this.target as AbstractSceneGrid;
            GUILayout.BeginVertical();
            if (this.DrawEditMode())
            {
                if (this.EditMode != null) abstractSceneGrid.FillGridColor(p => this.EditMode.GetCellColor(p));
            }
            if (this.EditMode != null) this.EditMode.OnInspectorGUI();
            GUILayout.EndVertical();
        }

        private void SaveRecordPos()
        {
            string filepath = Path.Combine(Application.dataPath, "../Log/editor_map_record_pos.txt");
            string dirpath = Path.GetDirectoryName(filepath);
            if (!Directory.Exists(dirpath)) Directory.CreateDirectory(dirpath);

            string[] temps = new string[recordPosList.Count];
            int index = 0;
            foreach(var p in recordPosList)
            {
                temps[index++] = string.Format("{0},{1}", p.x, p.y);
            }

           
            File.WriteAllLines(filepath, temps);
        }

        private void OpenRecordPosFile()
        {
            string filepath = Path.Combine(Application.dataPath, "../Log/editor_map_record_pos.txt");
            string dirpath = Path.GetDirectoryName(filepath);
            if (!Directory.Exists(dirpath))
            {
                UnityEngine.Debug.LogError("File is not exist :" + dirpath);
                return;
            }
            //启动一进程打开文件
            new Process
            {
                StartInfo = { FileName = filepath }

            }.Start();
         }
    }
}

